package com.base.springboot.starter.log.config;

import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.serializer.JSONSerializer;
import com.alibaba.fastjson.serializer.SerializeWriter;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.multipart.MultipartRequest;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * 方法切面基础对象
 *
 * @author admin
 * @date 2020/5/20 PM12:02
 */
@Slf4j
public abstract class AbstractMethodInvocation {
    /**
     * <p>Title: getQualiFiedName.</p>
     * <p>Description: 获取被代理类的全限定名</p>
     *
     * @param pjp 切面对象
     * @return 全限定名
     */
    protected String getQualiFiedName(ProceedingJoinPoint pjp) {
        return pjp.getTarget().getClass().getName();
    }

    /**
     * <p>Title: getMethodName.</p>
     * <p>Description: 获取当前切面代理的方法名称</p>
     *
     * @param pjp 切面对象
     * @return 方法名称
     */
    protected String getMethodName(ProceedingJoinPoint pjp) {
        return pjp.getSignature().getName();
    }

    /**
     * <p>Title: getArgsClassName.</p>
     * <p>Description: 获取请求参数的类名，如果没哟uqingqiu参数，将会返回NULL</p>
     *
     * @param pjp 切面对象
     * @return 入参类型名称，如果出现NULL对象，使用“NULL-OBJECT”字符表示
     */
    protected String[] getArgsClassName(ProceedingJoinPoint pjp) {
        Object[] args = pjp.getArgs();
        if (ArrayUtil.hasEmpty(args)) {
            return new String[]{};
        }
        String[] result = new String[args.length];
        for (int i = 0; i < result.length; i++) {
            if (ObjectUtil.isNotNull(args[i])) {
                result[i] = args[i].getClass().getName();
            } else {
                result[i] = "NULL-OBJECT";
            }
        }
        return result;
    }

    /**
     * <p>Title: getResultValue.</p>
     * <p>Description: 以json字符串的方式描述结果对象。
     * 如果返回值为NULL，直接使用“NULL”表示；如果是不能进行转换的对象，使用“UNPARSE_OBJECT”表示。
     * 如果解析过程中发生异常，将会返回“PARSE_ERROR”字符串。</p>
     *
     * @param result 结果对象
     * @return json字符串的描述形式
     */
    protected String getResultValue(final Object result) {
        return toJSONString(result);
    }

    /**
     * <p>Title: getArgsValue.</p>
     * <p>Description: 以JSON字符串的方式描述请求参数集合。如果入参对象或其中某个对象为NULL，使用“NULL”表示；如果是不能进行转换的对象，使用“UNPARSE_OBJECT”标识。</p>
     *
     * @param args 请求参数集合
     * @return json字符串的描述形式
     */
    protected String getArgsValue(final Object[] args) {
        if (ArrayUtil.hasEmpty(args)) {
            return "NULL";
        }

        int argsLength = args.length;
        String[] argsDataStr = new String[argsLength];

        for (int i = 0; i < argsLength; i++) {
            String tmp = toJSONString(args[i]);
            argsDataStr[i] = tmp;
        }

        return ArrayUtil.toString(argsDataStr);
    }

    /**
     * <p>Title: needFilter.</p>
     * <p>Description: 判断数据对象是否可以进行JSON的方式转换输出。当前设定HttpServletRequest、HttpServletResponse、byte[]为不能转换对象 </p>
     *
     * @param obj 待验证的数据对象
     * @return 验证结果，不可输出返回false，可以输出返回true
     */
    protected boolean isPrintableObj(final Object obj) {
        if (null == obj || obj instanceof MultipartFile
                || obj instanceof MultipartRequest || obj instanceof MultipartHttpServletRequest) {
            return false;
        }

        return !(obj instanceof HttpServletRequest || obj instanceof HttpServletResponse || obj instanceof byte[]);
    }

    /**
     * <p>Title: toJSONString.</p>
     * <p>Description: 将数据对象转换为json字符串格式，过程中会调用needFilter方法，忽略指定的类型。
     * 如果是需要忽略的类型此方法返回：UNPARSE_OBJECT；如果发生转换异常返回：PARSE_ERROR；如果是null返回：NULL；当对象中可输出属性值得toString长度大于64KB时，将会忽略该属性。</p>
     *
     * @param obj 待转换的对象
     * @return 转换后的json字符串
     */
    protected String toJSONString(final Object obj) {
        if (obj == null) {
            return "NULL";
        }

        //判断整个待转换对象的类型
        if (!isPrintableObj(obj)) {
            return "UNPARSE_OBJECT";
        }

        SerializeWriter out = new SerializeWriter();
        try {
            JSONSerializer serializer = new JSONSerializer(out);
            //自定义属性过滤规则
            serializer.getPropertyFilters().add((object, name, value) -> {
                //判断属性是否在过滤的类型中，并且取值的toString长度不能超过64kb，放置输出内容较多
                if (value == null) {    //输出为NULL的属性
                    return true;
                } else {
                    return isPrintableObj(value) && value.toString().length() <= 1024 * 64;
                }
            });
            serializer.write(obj);
            return out.toString();
        } catch (Exception e) {
            log.error("[ecp-base][method] 转换参数为JSON格式发生错误：", e.getMessage(), e);
            return "PARSE_ERROR";
        } finally {
           out.close();
        }
    }

}
